Docly Developers
Knowledge base Pitfall By design

Template operators differ from JavaScript in compatibility mode

In shortA .hash page runs in compatibility mode unless the site turns it off. In that mode Docly's older template operators live on top of JavaScript, and four of them reuse JavaScript's characters: ^= and ~= mean "contains", ^! and ~! mean "does not contain". Where the value is used — if (a ^= b), #a ^= b#, x = (a ^= b) — it is a text comparison, not an XOR. Since October 2026, h ^= x; standing alone on a line is an XOR assignment in both modes; before that it was a comparison whose result was thrown away, and the line silently did nothing. A bare ^ or ~ is plain XOR and NOT in both modes. Write h = h ^ x, put a space in a ^ !b, or switch the page to plain JavaScript — recommended for every new site and template: "hashjs": { "compatibilityMode": false } in site.json for the site's .hash pages, or Modern syntax on a schema template.
Applies to HASH filesTemplates
  1. What you'll see

    JavaScript that is correct in an API file behaves differently in a .hash page:

    #{
      let h = 5381;
      for (let i = 0; i < text.length; i++) {
        h = (h ^= text.charCodeAt(i)) * 33;   // ^= is a comparison here in a .hash page
      }
    }#

    No error, no warning — h gets a meaningless value, because h ^= … inside an expression is a comparison. The same code in #/API/ changes h as expected.

    Related symptoms in the same family:

    • let y = ~!x; fails to compile with parsing failed of operator '~!'.
    • if (a ^!b) is read as "a does not contain b", not as a ^ (!b).
  2. What's actually happening

    .hash pages outside #/API/ run in compatibility mode: plain JavaScript plus Docly's older template syntax. That syntax includes text operators made of two characters:

    OperatorIn compatibility modeIn plain JavaScript
    a ^= ba contains b (case-sensitive)XOR assignment
    a ~= ba contains b (not case-sensitive)syntax error
    a ^! ba does not contain b (case-sensitive)a ^ (!b)
    a ~! ba does not contain b (not case-sensitive)syntax error
    a ^ b, ~aXOR, NOTXOR, NOT

    The pair wins whenever the two characters touch. Inside an expression, h ^= x is therefore a comparison: it evaluates to true or false, and h is not assigned. That is why there is no error. The one exception is h ^= x; standing alone on a line: since October 2026 (#29249) that is an XOR assignment in both modes. Before that it was also a comparison, and the line did nothing — code that relied on that no-op changes behaviour.

    A single ^ or ~ has no template meaning, so it is plain bitwise XOR and NOT in both modes. (Older Docly versions rejected a bare ^ or ~ in a .hash page with Unknown token '^' in expression. If you still see that error, write the XOR as ((a | b) - (a & b)), or move the code to an API file.)

  3. What to do

    Write the assignment out. It works the same in both modes:

    h = h ^ text.charCodeAt(i);          // not: h ^= ...
    h = ((h * 33) ^ text.charCodeAt(i)) >>> 0;   // a typical string hash

    Separate characters you mean separately: a ^ !b with a space, and ~(!x) instead of ~!x.

    On a new site, avoid the pitfall entirely by starting in the strictest mode — see Start new sites in strict JavaScript mode.

    Or turn compatibility mode off, and the page is plain JavaScript, exactly like an API file:

    • For the site's .hash pages: "hashjs": { "compatibilityMode": false } in #/site.json. See Site.json config file.
    • For a template stored in a schema: tick Modern syntax in that template's settings in the schema editor.

    Turning it off also removes the bracket conditions (#[Field]#…#[/Field]#), the other text operators ($= and friends), case-insensitive function names and .Length/.Count. Check that the pages compile before switching a live site.

    When a .hash page computes something different from the same code in an API file, suspect a two-character operator first. Test the expression on its own in a scratch page — see Use a scratch hash file to test Docly functions.

Last reviewed 07 Oct 2026